﻿//===========================[ 8 / 2 / 2011 LVI ]=============================
//				Copyright c Vitaly Lyaschenko <SkyChameleon@gmail.com>
//								Source file
// Purpose: 
//=============================================================================
#include "cMeshManager.h"
#include "cwLogger.h"
#include "mathlib.h"
#include "common.h"
#include "lib3ds.h"

cMeshesManager::cMeshesManager()
{
	cwLogger::writeINFO("< Mesh Manager > Instance Create.");
}

cMeshesManager::~cMeshesManager()
{
	cwLogger::writeINFO("< Mesh Manager > Instance Destroy.");
}

bool cMeshesManager::weldVertices( cSubMesh *subMesh )
{
	std::vector<Vertex_PNT> vb;
	std::vector<_16ubit> ib;

	ulong faceCout = subMesh->getNumFaces();
	// для всех полигонов в мэше
	for( ulong face = 0; face < faceCout; face++ )
	{
		// текущий полигон
		for ( uint i = 0; i < 3; i++ )
		{
			bool eqv = false;
			// текущая вершина полигона
			Vertex_PNT c_Vert = subMesh->m_Vertices[ face * 3 + i ];
			ulong _ii = 0;
			// ищем совпадения вершин
			for( ; _ii != vb.size(); _ii++ )
			{   // нашли ? добавляем только индекс
				if ( c_Vert == vb[ _ii ] )
				{
					ib.push_back( _ii );
					eqv = true;
				}
			}
			// если есть такая вершина -> добавлять ничего небудем
			// иначе добавляем новую вершину и ее индекс
			if ( eqv == true )
				continue;
			vb.push_back( c_Vert );
			ib.push_back( _ii );
		}
	}

	subMesh->m_Vertices.clear();
	subMesh->m_Indices.clear();

	subMesh->m_Vertices.assign( vb.begin(), vb.end() );
	subMesh->m_Indices.assign( ib.begin(), ib.end() );

	return true;
}

bool cMeshesManager::optimize( cMesh *model, optType ot )
{
	if ( model->hData.numMesh < 1 )
		return false;

	switch(ot)
	{
	case WELD_VERTICES:
		{   // оптимизируем все сетки в модели
			for ( uint i = 0; i != model->hData.numMesh; i++ )
				weldVertices( model->meshes[ i ] );
			model->hData.m_format = WELD_VERTICES;
		} break;
	case CACHE_FACES_OPTIMIZE:
		{
			model->hData.m_format = CACHE_FACES_OPTIMIZE;
		}break;
	case CACHE_VERTICES_OPTIMIZE:
		{

		}break;
	case TOTAL_OPTIMIZE:
		{

		}break;
	}
	return true;
}

cMesh* cMeshesManager::load_3DS( const std::string& fileName )
{
	Lib3dsFile * _3dsFile;

	// загружаем 3ds файл
	_3dsFile = lib3ds_file_load( fileName.c_str() );

	// если загрузка не удалась ---> false
	if( !_3dsFile )
	{
		cwLogger::writeERROR("cannot open to read (*.3DS) file : " + std::string(fileName) );
		return false;
	}

	Lib3dsMesh *MESH;

	cMesh *pMesh = new cMesh; 

	// выделяем память под данные модели
	for( MESH = _3dsFile->meshes; MESH != NULL; MESH = MESH->next )
	{
		pMesh->createMesh();
		pMesh->lastMesh()->setNumVertices( MESH->faces * 3 );
	}

	uint total_mesh = 0;
	// в цикле обрабатываются все элементы модели (sub Model mesh)
	for( MESH = _3dsFile->meshes; MESH != NULL; MESH = MESH->next )
	{
		ulong FinishedFaces = 0;

		// вычислить нормалиy
		// инициализация нормалей
		ulong numVert = pMesh->meshes[ total_mesh ]->getNumVertices();
		std::vector<vec3> Normal_tmp( numVert, vec3_zero );

		lib3ds_mesh_calculate_normals( MESH, (Lib3dsVector*)&Normal_tmp.front() );

		// цикл для каждого полигона
		for ( ulong _face = 0; _face != MESH->faces; _face++ )
		{
			// цикл для каждой вершине в полигоне
			Lib3dsFace *face = &MESH->faceL[ _face ];

			// копирование вершин для текущего полигона 1..3
			for ( uint i = 0; i < 3; i++ )
			{
				Vertex_PNT _Vert;
				uint m_points = face->points[i];
				// вычисление индекса вершины
				_16ubit ind = FinishedFaces * 3 + i;

				// копирование 3х координат для вершины
				//_Vert.x = MESH->pointL[ m_points ].pos[ 0 ];
				//_Vert.y = MESH->pointL[ m_points ].pos[ 1 ];
				//_Vert.z = MESH->pointL[ m_points ].pos[ 2 ];
				memcpy(&_Vert, MESH->pointL[ m_points ].pos, sizeof(float) * 3 );

				// получаем индекс вершины ( номер в массиве vertices )
				pMesh->meshes[ total_mesh ]->setIndex( ind );

				// копирование 3х координат для нормали из временного контейнера в структуру вертекса
				//_Vert.nx = Normal_tmp[ m_points ].x;
				//_Vert.ny = Normal_tmp[ m_points ].y;
				//_Vert.nz = Normal_tmp[ m_points ].z;
				memcpy(&_Vert.nx, Normal_tmp[ m_points ].v_arr, sizeof(float) * 3 );

				// если вершина имеет текстурные координаты
				if(MESH->texels)
				{
					// копирование текстурных координат
					//_Vert.u = MESH->texelL[ m_points ][0];
					//_Vert.v = MESH->texelL[ m_points ][1];
					memcpy(&_Vert.u, MESH->texelL[  m_points ], sizeof(float) * 2 );
				}
				// передаем собранную вершину
				pMesh->meshes[ total_mesh ]->setVertex( _Vert );
			}
			FinishedFaces++; // увеличиваем количество скопированных полигонов
		}
		total_mesh++;        // переходим к следующему  элементу(Mesh)
		Normal_tmp.clear();
	}

	cwLogger::writeINFO("File : " + std::string(fileName) + " Load!" );

	// удаляем данные файла из памяти
	lib3ds_file_free(_3dsFile);

	return pMesh;
}

cMesh* cMeshesManager::load_IFM( const std::string& fileName )
{
	if ( fileName.empty() )
		return false;

	ulong numVert, numIndx;
	std::ifstream inFile;
	inFile.open( fileName, std::ios::binary | std::ios::in );

	if ( !inFile.is_open() )
		cwLogger::writeERROR("cannot open to read (*.ifm) file : "+std::string(fileName) );

	cMesh *pModel = new cMesh;

	inFile.read( (char*)&pModel->hData, sizeof(HeaderModel) );

	for ( uint i = 0; i != pModel->hData.numMesh; i++ )
	{
		pModel->meshes.push_back( new cSubMesh );

		inFile.read( (char*)&numVert, sizeof(ulong) );
		inFile.read( (char*)&numIndx, sizeof(ulong) );

		pModel->meshes[ i ]->setNumVertices( numVert );
		pModel->meshes[ i ]->setNumIndices( numIndx );

		inFile.read( (char*)&pModel->meshes[i]->beginVert(), sizeof(Vertex_PNT) * numVert );
		inFile.read( (char*)&pModel->meshes[i]->beginIndx(), sizeof(_16ubit) * numIndx );
	}

	inFile.close();

	cwLogger::writeINFO( "File : " + std::string(fileName) + " Load!" );

	return pModel;
}

bool cMeshesManager::save_IFM( const std::string& fileName, cMesh *model )
{
	if( fileName.empty() )
		return false;

	std::ofstream outFile;

	outFile.open( fileName + ".IFM", std::ios::binary | std::ios::out );

	if( !outFile.is_open() )
	{
		cwLogger::writeERROR("Cannot open to rewrite file : " + std::string(fileName) );
		return false;
	}

	ulong numVert, numIndx;

	outFile.write( (char*)&model->hData, sizeof(HeaderModel) );

	for ( uint i = 0; i != model->hData.numMesh; i++ )
	{
		numVert = model->meshes[ i ]->getNumVertices();
		numIndx = model->meshes[ i ]->getNumIndices();

		outFile.write( (char*)&numVert, sizeof(ulong) );
		outFile.write( (char*)&numIndx, sizeof(ulong) );

		outFile.write( (char*)&model->meshes[ i ]->beginVert(), sizeof(Vertex_PNT) * numVert );
		outFile.write( (char*)&model->meshes[ i ]->beginIndx(), sizeof(_16ubit) * numIndx );
	}
	outFile.close();

	cwLogger::writeINFO( "File : " + std::string( fileName + ".IFM" ) + " Save!" );

	return true;
}
///////////////////////////////////////////////////////////////////////////
// загрузка 3d модели из файла
cMesh* cMeshesManager::loadMesh( const std::string& fileName )
{
	// получаем расширение файла
	std::string fileExt = cFileManager::extractFileExtension( fileName );

	if ( (fileExt == "3ds") || (fileExt == "3DS") )
		return load_3DS( fileName );

	if ( (fileExt == "ifm") || (fileExt == "IFM") ) // ifmo - Inner Format Optimization Model
		return load_IFM( fileName );

	cwLogger::writeERROR( "Wrong format mesh file : " + std::string(fileExt) );

	return false;
}

bool cMeshesManager::saveMesh( const std::string& fileName, cMesh *mesh )
{
	if ( fileName.empty() || ( uint(mesh->getNumMesh()) == 0 ) )
		return false;

	std::string exten = cFileManager::extractFileExtension( fileName );
	// если расширения не совпадают
	if ( (exten != ".ifm") || (exten != ".IFM") )
	{
		const std::string name = cFileManager::extractFileName( fileName );
		return save_IFM( name, mesh );      // TODO :- done!
	}
	return false;
}